<template>
	<el-cascader
			:clearable="_clearable"
			:collapse-tags="_collapseTags"
			:collapse-tags-tooltip="_collapseTagsTooltip"
			:disabled="_disabled"
			:filterable="_filterable"
			:max-collapse-tags="_maxCollapseTags"
			:options="options"
			:placeholder="_placeholder"
			:popper-class="_popperClass"
			:props="_settings"
			:show-all-levels="_showAllLevels"
			class="business-code-cascader"
			v-bind="$attrs"
			@change="modelValueChanged"
	>
		<template v-for="(value, name) in $slots" #[name]="scope">
			<slot :name="name" v-bind="scope"/>
		</template>
	</el-cascader>
</template>

<script lang="ts" setup>

import {computed, defineSlots} from 'vue'
import {getBusinessCodeData, treeArrayIterator} from '@/utils/commonUtil'
import type {CascaderProps} from 'element-plus'
import {cloneDeep} from 'lodash-es'

interface Props {
	codeType: string
	fullPathValue?: boolean
	showAllLevels?: boolean
	collapseTags?: boolean
	maxCollapseTags?: number
	collapseTagsTooltip?: boolean
	hideValue?: boolean
	disabled?: boolean
	settings?: CascaderProps
	disabledValueArray?: BusinessCodeTreeNode['value'][]
	hideValueArray?: BusinessCodeTreeNode['value'][]
	placeholder?: string
	popperClass?: string
	clearable?: boolean
	filterable?: boolean
	forceStringValue?: boolean
}

interface OptionItem {
	label: string
	value: BusinessCodeOptionValue
	children: OptionItem[]
	disabled: boolean
	leaf?: boolean
}

const props = withDefaults(defineProps<Props>(), {
	fullPathValue: false,
	showAllLevels: false,
	collapseTags: true,
	maxCollapseTags: 3,
	collapseTagsTooltip: true,
	hideValue: true,
	settings: () => DEFAULT_CASCADER_SETTINGS,
	disabled: false,
	placeholder: '请选择',
	clearable: true,
	filterable: true,
	forceStringValue: false,
	disabledValueArray: () => [],
	hideValueArray: () => []
})

defineSlots<{
	default: any
	empty: any
}>()

const emits = defineEmits<{
	(e: 'update:modelValue', value?: BusinessCodeCascaderValue): void
	(e: 'change', value?: BusinessCodeCascaderValue): void
}>()

const _clearable = computed(() => props.clearable)
const _collapseTags = computed(() => props.collapseTags)
const _collapseTagsTooltip = computed(() => props.collapseTagsTooltip)
const _disabled = computed(() => props.disabled)
const _filterable = computed(() => props.filterable)
const _placeholder = computed(() => props.placeholder)
const _popperClass = computed(() => props.popperClass)
const _settings = computed(() => {
	return Object.assign({}, DEFAULT_CASCADER_SETTINGS, props.settings)
})
const _showAllLevels = computed(() => props.showAllLevels)
const _maxCollapseTags = computed(() => props.maxCollapseTags)

const options = computed<OptionItem[]>(() => {
	const businessCodeData = getBusinessCodeData(props.codeType)
	if (!businessCodeData || businessCodeData.length === 0) {
		return []
	}
	let roots = <OptionItem[]><unknown>cloneDeep(businessCodeData)
	// 需要隐藏的数据
	roots = roots.filter(node => !props.hideValueArray.includes(<string>node.value))
	treeArrayIterator(roots, node => {
		if (node.children && node.children.length > 0) {
			node.children = node.children.filter(node => !props.hideValueArray.includes(props.fullPathValue ? <string>node.value : (<string>node.value).substring((<string>node.value).lastIndexOf('.') + 1)))
		}
	})
	// 为disabled和leaf属性赋值，并根据hideValue处理label
	treeArrayIterator(roots, node => {
		node.label = props.hideValue ? node.label : `${node.value} : ${node.label}`
		node.value = handleValue(<string>node.value)
		node.disabled = props.disabledValueArray.includes(<string>node.value)
		node.leaf = !node.children || node.children.length === 0
	})
	return roots
})

const handleValue = (value: string): BusinessCodeOptionValue => {
	if (!props.fullPathValue) {
		value = value.substring(value.lastIndexOf('.') + 1)
	}
	if (props.forceStringValue) {
		return value
	}
	// 尝试转换为boolean
	if (value === 'true' || value === 'false') {
		return JSON.parse(value)
	}
	// 尝试转换为number
	const numberValue = Number(value)
	if (Number.isSafeInteger(numberValue)) {
		return numberValue
	}
	// 其他情况使用string
	else {
		return value
	}
}

/**
 * 向父组件传值
 */
const modelValueChanged = (value: BusinessCodeCascaderValue) => {
	emits('update:modelValue', value)
	emits('change', value)
}

</script>

<script lang="ts">
import type {CascaderProps as ElCascaderProps} from 'element-plus'

const DEFAULT_CASCADER_SETTINGS: ElCascaderProps = {
	/**
	 * 次级菜单的展开方式
	 */
	expandTrigger: 'hover',

	/**
	 * 是否多选
	 */
	multiple: false,

	/**
	 * 是否严格的遵守父子节点不互相关联
	 */
	checkStrictly: false,

	/**
	 * 在选中节点改变时，是否返回由该节点所在的各级菜单的值所组成的数组，若设置 false，则只返回该节点的值
	 */
	emitPath: false,

	/**
	 * 是否动态加载子节点，需与 lazyLoad 方法结合使用
	 */
	lazy: false,

	/**
	 * 加载动态数据的方法，仅在 lazy 为 true 时有效
	 */
	lazyLoad: undefined,

	/**
	 * 指定选项的值为选项对象的某个属性值
	 */
	value: 'value',

	/**
	 * 指定选项标签为选项对象的某个属性值
	 */
	label: 'label',

	/**
	 * 指定选项的子选项为选项对象的某个属性值
	 */
	children: 'children',

	/**
	 * 指定选项的禁用为选项对象的某个属性值
	 */
	disabled: 'disabled',

	/**
	 * 指定选项的叶子节点的标志位为选项对象的某个属性值
	 */
	leaf: 'leaf',

	/**
	 * hover 时展开菜单的灵敏度阈值，单位为 ms
	 */
	hoverThreshold: 500
}
</script>

<style lang="scss" scoped>
.business-code-cascader {
	width: 100%;
}
</style>
